import java.util.*;

/*
一共有 n个数，编号是 1∼n，最开始每个数各自在一个集合中。
现在要进行 m 个操作，操作共有两种：
M a b，将编号为 a 和 b 的两个数所在的集合合并，如果两个数已经在同一个集合中，则忽略这个操作；
Q a b，询问编号为 a 和 b 的两个数是否在同一个集合中；

输入格式
第一行输入整数 n 和 m。
接下来 m 行，每行包含一个操作指令，指令为 M a b 或 Q a b 中的一种。

输出格式
对于每个询问指令 Q a b，都要输出一个结果，如果 a 和 b 在同一集合内，则输出 Yes，否则输出 No。
每个结果占一行。

数据范围
1≤n,m≤10^5

输入样例：
4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4

输出样例：
Yes
No
Yes
 */

//public class Main {
//    static int[] p = new int[1010];
//
//    static int find(int a) {
//        if (a != p[a]) p[a] = find(p[a]);
//        return p[a];
//    }
//
//    static void merge(int a, int b) {
//        p[find(a)] = find(b);
//    }
//
//    static boolean query(int a, int b) {
//        return find(a) == find(b);
//    }
//
//    public static void main(String[] args) {
//        Scanner sc = new Scanner(System.in);
//        int n = sc.nextInt(), m = sc.nextInt();
//        for (int i = 1; i <= n; i++) {
//            p[i] = i;
//        }
//        for (int i = 1; i <= m; i++) {
//            String op = sc.next();
//            if (op.equals("M")) {
//                int a = sc.nextInt(), b = sc.nextInt();
//                merge(a, b);
//            } else if (op.equals("Q")) {
//                int a = sc.nextInt(), b = sc.nextInt();
//                if (query(a, b)) {
//                    System.out.println("Yes");
//                } else {
//                    System.out.println("No");
//                }
//            }
//        }
//    }
//}

/*
给定一个包含 n 个点（编号为 1∼n）的无向图，初始时图中没有边。
现在要进行 m 个操作，操作共有三种：
C a b，在点 a 和点 b 之间连一条边，a 和 b 可能相等；
Q1 a b，询问点 a 和点 b 是否在同一个连通块中，a和 b 可能相等；
Q2 a，询问点 a 所在连通块中点的数量；

输入格式
第一行输入整数 n 和 m。
接下来 m 行，每行包含一个操作指令，指令为 C a b，Q1 a b 或 Q2 a 中的一种。

输出格式
对于每个询问指令 Q1 a b，如果 a 和 b 在同一个连通块中，则输出 Yes，否则输出 No。
对于每个询问指令 Q2 a，输出一个整数表示点 a 所在连通块中点的数量
每个结果占一行。

数据范围
1≤n,m≤10^5

输入样例：
5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5

输出样例：
Yes
2
3
 */
//public class Main {
//    static int[] p = new int[1010];//记录并查集
//    static int[] q = new int[1010];//记录点数
//    static int find(int a) {
//        if (a != p[a]) p[a] = find(p[a]);
//        return p[a];
//    }
//
//    static void merge(int a, int b) {
//        if(find(a) != find(b)) {
//            q[find(b)] += q[find(a)];
//            p[find(a)] = find(b);
//        }
//    }
//
//    static boolean query(int a, int b) {
//        return find(a) == find(b);
//    }
//
//    public static void main(String[] args) {
//        Scanner sc = new Scanner(System.in);
//        int n = sc.nextInt(), m = sc.nextInt();
//        for (int i = 1; i <= n; i++) {
//            p[i] = i;
//            q[i] = 1;
//        }
//        for (int i = 1; i <= m; i++) {
//            String op = sc.next();
//            switch (op) {
//                case "C": {
//                    int a = sc.nextInt(), b = sc.nextInt();
//                    merge(a, b);
//                    break;
//                }
//                case "Q1": {
//                    int a = sc.nextInt(), b = sc.nextInt();
//                    if (query(a, b)) {
//                        System.out.println("Yes");
//                    } else {
//                        System.out.println("No");
//                    }
//                    break;
//                }
//                case "Q2": {
//                    int a = sc.nextInt();
//                    System.out.println(q[find(a)]);
//                    break;
//                }
//            }
//        }
//    }
//}

/*
动物王国中有三类动物 A,B,C，这三类动物的食物链构成了有趣的环形。
A吃 B，B吃 C，C吃 A。
现有 N个动物，以 1∼N 编号。
每个动物都是 A,B,C 中的一种，但是我们并不知道它到底是哪一种。
有人用两种说法对这 N个动物所构成的食物链关系进行描述：
第一种说法是 1 X Y，表示 X 和 Y 是同类。
第二种说法是 2 X Y，表示 X 吃 Y。
此人对 N个动物，用上述两种说法，一句接一句地说出 K句话，这 K句话有的是真的，有的是假的。
当一句话满足下列三条之一时，这句话就是假话，否则就是真话。
当前的话与前面的某些真的话冲突，就是假话；
当前的话中 X或 Y比 N大，就是假话；
当前的话表示 X吃 X，就是假话。
你的任务是根据给定的 N和 K句话，输出假话的总数。

输入格式
第一行是两个整数 N和 K，以一个空格分隔。
以下 K行每行是三个正整数 D，X，Y，两数之间用一个空格隔开，其中 D表示说法的种类。
若 D=1，则表示 X和 Y是同类。
若 D=2，则表示 X吃 Y。

输出格式
只有一个整数，表示假话的数目。

数据范围
1≤N≤50000,0≤K≤100000

输入样例：
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5

输出样例：
3
 */

public class Main {
    static int n, k;
    static int[] p = new int[50005];//维护并查集
    static int[] d = new int[50005];//维护到父节点的距离

    static int find(int x) {
        if (x != p[x]) {
            int t = find(p[x]);
            d[x] += d[p[x]];
            p[x] = t;
        }
        return p[x];
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        k = sc.nextInt();
        for (int i = 1; i <= n; i++) {
            p[i] = i;
        }
        int count = 0;
        while (k-- > 0) {
            int D = sc.nextInt(), x = sc.nextInt(), y = sc.nextInt();
            int px = find(x), py = find(y);
            if (x > n || y > n || (D == 2 && x == y)) count++;
            else if (D == 1) {
                if (px == py && (d[x] - d[y]) % 3 != 0) count++;//若x与y非同类
                else if (px != py) {
                    p[px] = py;
                    d[px] = d[y] - d[x];
                }
            } else if (D == 2) {
                if (px == py && (d[x] - d[y] - 1) % 3 != 0) count++;//若非x吃y
                else if (px != py) {
                    p[px] = py;
                    d[px] = d[y] + 1 - d[x];
                }
            }

        }
        System.out.println(count);

    }
}